/**
* @file Level.cpp
* Implementation of the level class
* @author Timmy Nelen
* @date 18/11/2010
*/

#include "Level.h"

/**
 * Constructor of a level object (empty)
 */
bo::Level::Level() {

}

/**
 * Destructor of a level object
 */
bo::Level::~Level() {
	delete fBall;
	delete fPaddle;
}

/**
 * Adds an entity to the level
 *
 * @param[in] ent The entity which is to be added to the level
 */
void bo::Level::AddToLevel(Entity* ent){
	fEntList.push_back(ent);
}

/**
 * Iterates over the vector and visualizes every entity
 */
void bo::Level::Visualize(){
	for (list<bo::Entity*>::iterator it = this->fEntList.begin(); it!=this->fEntList.end(); ++it) {
		(*it)->Visualize();
	}
	for (list<PowerUp*>::iterator it = this->fPowerUpList.begin(); it!=this->fPowerUpList.end(); ++it) {
		(*it)->Visualize();
	}
	this->fBall->Visualize();
	this->fPaddle->Visualize();
}

/**
 * Adds a ball to the level
 *
 * @param[in] ball The ball which is to be added
 */
void bo::Level::AddBall(Ball* ball){
	this->fBall = ball;
}

/**
 * Adds the paddle to the level
 *
 * @param[in] paddle The paddle which is to be used
 */
void bo::Level::AddPaddle(Paddle* paddle){
	this->fPaddle = paddle;
}

/**
 * "Releases" a powerup -> puts one from the availables ones into the actual game
 *
 * @param[in] pos The position where the powerup has to be released
 */

void bo::Level::ReleasePowerUp(Vector2 pos){
	srand(time(NULL));
	if ( this->fPowerUpList.size() == 0 ){
		return;
	}
	else{
		list<PowerUp*>::iterator it = this->fPowerUpList.begin();
		int random = rand() % 100;
		if ( random < (*it)->GetProbability() )
			(*it)->AddLocation(pos);
	}
}



/**
 * Checks for collisions between the ball and the rest of the entities, IF collision: Let the ball bounce
 *
 * @param[in] player The player which is playing the game
 */
void bo::Level::CheckCollisions(Player* player){

	for (list<PowerUp*>::iterator it = this->fPowerUpList.begin(); it!=this->fPowerUpList.end(); ++it){
		if ( (*it)->CollidesWith(this->fPaddle) ){
			if ( !player->GetPowerUpActive()){
				player->SetPowerUpActive();
				this->fPaddle->SetSingleVelocity(this->fPaddle->GetVelocity() * 3);
			}
		}
	}


	if ( this->fBall->GetPos().GetY() > -0.5 ){
		for (list<bo::Entity*>::iterator it = this->fEntList.begin(); it!=this->fEntList.end(); ++it) {
			if ( this->fBall->CollidesWith(*it) ){
				this->fBall->Bounce( (*it) );
				if (dynamic_cast<Breakable*>(*it)){
					Vector2 tempPos = (*it)->GetPos();
					delete *it;
					it = this->fEntList.erase(it);
					player->AddPoints(10);
					srand(time(NULL));
					if ( !player->GetPowerUpActive() ){
						this->ReleasePowerUp( tempPos );
					}
				}
			}
		}
	}
	else if ( this->fBall->GetPos().GetY() <= 0 ){
		if ( this->fBall->CollidesWith(this->fPaddle) ){
			this->fBall->Bounce(this->fPaddle);
		}
	}
}

/**
 * Advances the state of the level by 1 step and resolves out of bounds errors, and processes key input
 *
 * @param[in, out] levelReturn The returnvalue of the level
 * 	- 0: Next level
 * 	- 1: Game over
 * 	- 2: Quit
 * 	- 3: Continue
 * @param[in] player The player which is playing the game
 */
void bo::Level::AdvanceLevelState(int& levelReturn, Player* player){
	int keyInput = this->ProcessLevelInput();
	if ( keyInput == 0 || keyInput == 1 ){
		fPaddle->UpdatePos(keyInput);
	}
	else if ( keyInput == 2){
		levelReturn = 2;
		return;
	}
	//else if ( keyInput == -1)
	//	// Nothing, just advance
	else {}
	this->fBall->AdvanceBall();
	for (list<PowerUp*>::iterator it = this->fPowerUpList.begin(); it!=this->fPowerUpList.end(); ++it) {
		(*it)->Advance();
		if ( (*it)->GetPos().GetY() < -3 ){
			it = this->fPowerUpList.erase(it);
		}
	}
	if ( this->fBall->GetPos().GetX()-this->fBall->GetRadius() < -4 ){
		this->fBall->GetPos().SetX(-4);
		this->fBall->SetDoubleVelocity(-this->fBall->GetXVelocity() , this->fBall->GetYVelocity());
	}
	if ( this->fBall->GetPos().GetX()+this->fBall->GetRadius() > 4){
		this->fBall->GetPos().SetX(4);
		this->fBall->SetDoubleVelocity(-this->fBall->GetXVelocity() , this->fBall->GetYVelocity());
	}
	if ( this->fBall->GetPos().GetY() - this->fBall->GetRadius() < -3 ){
		// Ball Exits the screen -> Reset ball with random direction
		if ( player->GetLivesLeft() > 1 ){
			// If more than 0 lives left: Reset Ball. Else: Return false and stop
			player->DecLives();
			this->fBall->SetPos(0, -1.5);
			srand(time(NULL));
			double newXVel = rand() % 200-100;
			double newYVel = rand() % 20+200;
			this->fBall->SetDoubleVelocity(newXVel/3500, newYVel/3500);
		}
		else
			levelReturn = 1;
			return;
	}
	if ( this->fBall->GetPos().GetY() + this->fBall->GetRadius() > 3 ){
		this->fBall->GetPos().SetY(3);
		this->fBall->SetDoubleVelocity(this->fBall->GetXVelocity() , -this->fBall->GetYVelocity());
	}
	this->CheckCollisions(player);
	if ( this->fEntList.empty() ){
		levelReturn = 0;
		return;
	}
	if ( player->GetPowerUpActive() ){
		int returnValue;
		player->AdvancePowerUpTimer(returnValue);
		if (returnValue == 1){
			this->fPaddle->SetSingleVelocity(0.04);
		}
	}

	levelReturn = 3;
	return;
}

/**
 * Adds a powerup to the level
 *
 * @param[in] powerUp The powerup which is to be added
 */
void bo::Level::AddPowerUp(PowerUp* powerUp){
	this->fPowerUpList.push_back(powerUp);
}
